home *** CD-ROM | disk | FTP | other *** search
/ The Fatted Calf / The Fatted Calf.iso / Applications / Audio-DSP / NU / Source / ClassManager.m < prev    next >
Encoding:
Text File  |  1993-03-07  |  20.9 KB  |  718 lines

  1. /* Generated by Interface Builder */
  2.  
  3. #import "ClassManager.h"
  4. #import "MenuManager.h"
  5. #import "NuString.h"
  6. #import "PrefsManager.h"
  7. #import "FinderManager.h"
  8. #import <appkit/Application.h>
  9. #import <appkit/Text.h>
  10. #import <appkit/Panel.h>
  11. #import <appkit/NXBrowser.h>
  12. #import <appkit/NXBrowserCell.h>
  13. #import <defaults/defaults.h>
  14. #import <appkit/NXSplitView.h>
  15. #import <appkit/Matrix.h>
  16. #import <objc/objc-load.h> 
  17. // #import <objc/objc-runtime.h> // causes compiler errors in 3.0!
  18. #import <objc/Storage.h>
  19. #import <objc/List.h>
  20. #import <streams/streams.h>
  21. #import <libc.h>
  22. #import <strings.h>
  23. #include <sys/types.h>
  24. #include <sys/stat.h>
  25. #import "GlyphView.h"
  26. #import <regex.h>
  27.  
  28. extern id Nu ;
  29. extern  void NuGets(NXStream *aStream, char * aBuf) ;
  30. static id loadList ; // this global var points to a  list of
  31.                      // NuStrings containing
  32.              // the classnames of every loaded class
  33.  
  34. id  classWinList ;  // this global var contains a list of the id's of all
  35.                     // currently open class windows
  36.  
  37.  
  38. @implementation ClassManager: WorkspaceManager
  39.  
  40. int superClassName(char *filePath, char *superClass) 
  41. { // assumes filePath is the path to a .o file
  42.   // containing a class definition. Sets superClass
  43.   // to the name of its superclass. Returns 1 if
  44.   // successful, else 0.
  45.   struct mach_header mh ;
  46.   struct segment_command sc ;
  47.   struct section sect ;
  48.   struct objc_class oc ;
  49.   int fn, i,j ;
  50.   char c ;
  51.   if((fn = open(filePath, O_RDONLY)) < 0)
  52.     return 0 ;
  53.   lseek(fn, 0l, 0) ;
  54.   if(read(fn,&mh,sizeof(struct mach_header)) < sizeof(struct mach_header))
  55.     return 0  ; // short file
  56.   for(i = 0 ; i < mh.ncmds ; i++) // for each load command
  57.   { if(read(fn,&sc,sizeof(struct load_command)) < sizeof(struct load_command))
  58.        return 0 ; // short file
  59.     if(sc.cmd != LC_SEGMENT) // not a segment command, skip it
  60.       if(lseek(fn, sc.cmdsize - sizeof(struct load_command) ,L_INCR) == -1)
  61.         return 0 ; // can't seek
  62.     else // segment command...get rest of header
  63.     { if(read(fn,&sc.segname,sizeof(struct segment_command) - 
  64.            sizeof(struct load_command)) < sizeof(struct segment_command) - 
  65.            sizeof(struct load_command))
  66.          return 0 ; // short file
  67.       else
  68.       { for(j = 0 ; j < sc.nsects ; j++) // for each segment header
  69.         { if(read(fn,§,sizeof(struct section)) < sizeof(struct section))
  70.             return 0 ; // short file ;
  71.           if(!strcmp(sect.segname,"__OBJC") && 
  72.               !strcmp(sect.sectname,"__class"))
  73.           { // we've found the correct section and segment
  74.             if(lseek(fn,sect.offset,L_SET) == -1)
  75.               return 0 ; // can't seek
  76.             if(read(fn,&oc,sizeof(struct objc_class))
  77.                 < sizeof(struct objc_class))
  78.               return 0 ; // short file
  79.         if(lseek(fn,mh.sizeofcmds  + (long int) oc.super_class +
  80.                 sizeof(struct mach_header), L_SET) == -1)
  81.                return 0 ; // can't seek
  82.             i = 0 ;
  83.             while(read(fn,&c,1))
  84.             { superClass[i] =  c ;
  85.               if(!superClass[i++])
  86.               { close(fn) ;
  87.                 return 1 ;
  88.               }
  89.             }
  90.           }
  91.         }
  92.       }
  93.     }
  94.   }
  95.   close(fn) ;
  96.   return 0 ;
  97. }
  98.  
  99. + compileClass:(char *) aClass file:(char *) aFile
  100. { // compile file aFile containing .m file for
  101.   // class aClass.  return nil on error.
  102.   char buf[512] ;
  103.   const char *rootPath ;
  104.   char tmpFile[20] = "NuXXXXXX" ;
  105.   int rval ;
  106.   // use awk to create .h file
  107.   mktemp(tmpFile) ;
  108.   rootPath = NXGetDefaultValue([NXApp appName],"NuPath") ; 
  109.   sprintf(buf,
  110.   "awk '\\\n"
  111.   "/^#pragma .h/      { $1 = \"\";$2=\"\"; print}\\\n"
  112.   "/^@implementation/ { if(state == 0) {$1 = \"@interface\"; print ;state++} }\\\n"
  113.   "/^{/               { if(state == 1) state++ }\\\n"
  114.   "                  { if(state == 2) print }\\\n"
  115.   "/^}/               { if(state == 2) state++ }\\\n"
  116.   "/^[+-]/           { if(state == 3) print }\\\n"
  117.   "/^@end/       { print \"@end\" ; state = 0}'\\\n"
  118.    "  < %s > %s/.h/%s.h",
  119.     aFile, rootPath, aClass ) ;
  120.   system(buf) ;
  121.   // compile into .o file ... don't remove the -c !
  122.   sprintf(buf, "cc -c -I %s/.h  %s -o %s/.o/%s.o %s"
  123.      "  2> /tmp/%s",
  124.       rootPath,
  125.       NXGetDefaultValue([NXApp appName],"CompilerOptions"),
  126.      rootPath, aClass, aFile, tmpFile) ;
  127.   if(rval = system(buf)) // non-zero exit status == errors
  128.   { sprintf(buf,"/tmp/%s",tmpFile) ;
  129.     [Nu appendFileToTranscript: buf] ;
  130.     NXRunAlertPanel("Nu","Compilation errors: see Transcript Window\n",
  131.          NULL,NULL,NULL) ;
  132.   } 
  133.   sprintf(buf,"rm /tmp/%s 2> /dev/null",tmpFile) ;
  134.   system(buf) ;
  135.   if(rval == 0)
  136.     return self ;
  137.   else
  138.     return nil ;
  139. }
  140.  
  141.  
  142. + initialize ;
  143. // initialize the loadList
  144. { loadList = [List new] ;
  145.   classWinList = [List new] ;
  146.   return [super initialize] ;
  147. }
  148.  
  149. +(BOOL) isLoaded: (char *) aClass ;
  150. // returns YES iff aClass is currently loaded.
  151. { NXHashTable *class_hash = objc_getClasses();
  152.   NXHashState state = NXInitHashState(class_hash);
  153.   Class class;
  154.   while(NXNextHashState(class_hash, &state, (void **) &class))
  155.   { if(!strcmp(class->name,aClass))
  156.       return YES ; 
  157.   }
  158.   return NO ;
  159. }
  160.  
  161. + loadAll ;
  162. // loads all the classes on the loadList.  This is
  163. // called when unarchiving: first the loadList is
  164. // established from the archive, then this method
  165. // is invoked to load all these classes.  Returns
  166. // nil and unloads all classes if any class cannot
  167. // be loaded.  Does not rebuild debug files
  168. { int i, knt, moduleKnt ;
  169.   char buf[512] ;
  170.   id linkList ;
  171.   if(!loadList)  // loadList not initialized == nothing to load
  172.     return nil ; 
  173.   knt = [loadList count] ;
  174.   linkList = [NXGetNamedObject("PrefsManager",NXApp) linkList] ;
  175.   moduleKnt = [linkList count] ;
  176.   { char *moduleNames[moduleKnt+2] ;
  177.     for(i = 0 ; i < moduleKnt ; i++) // make list of libraries
  178.       moduleNames[i+1] = (char *) [[linkList objectAt: i] cString] ;
  179.     moduleNames[i+1] = NULL ; // terminate the list
  180.     for(i = 0 ; i < knt ; i++)
  181.     { sprintf(buf,"%s/%s.o",NXGetDefaultValue([NXApp appName],"NuPath"),
  182.             [[loadList objectAt: i] cString]) ;
  183.       moduleNames[0] = buf ;
  184.       if(objc_loadModules(moduleNames, NULL, NULL, NULL,NULL))
  185.       { NXRunAlertPanel("Nu","Error in loading, no classes loaded",
  186.            NULL,NULL,NULL) ;
  187.         [self unloadAll] ;
  188.         return self ;
  189.       }
  190.     }
  191.   }
  192.   return self ;
  193. }
  194.  
  195. +(int) loadIndex: (const char *) nameOfClass ;
  196. // if nameOfClass is in the load list, return its index,
  197. // else return -1.  The load list is a list of
  198. // all classes which have been dynamically loaded.
  199. { int i, knt ;
  200.   knt = [loadList count] ;
  201.   for(i = 0 ; i < knt ; i++)
  202.   { if(!strcmp([[loadList objectAt: i] cString], (char *) nameOfClass))
  203.       return i ;
  204.   }
  205.   return -1 ;
  206. }
  207.  
  208. + loadList ;
  209. { return loadList ;
  210. }
  211.  
  212. + loadList: aList ;
  213. { loadList = aList ;
  214.   return self ;
  215. }
  216.  
  217. + load: (const char *) aClass ;
  218. { // returns nil if aClass can't be loaded
  219.   int i, loadIndex, knt ;
  220.   id nmList, oldIdList, newIdList, gVList ;
  221.   char *ptr ;
  222.   id placeHolder, rval ;
  223.   rval = self ;
  224.   nmList = [[List alloc] init] ;
  225.   oldIdList = [[List alloc] init] ;
  226.   newIdList = [[List alloc] init] ;
  227.   gVList = [[List alloc] init] ;
  228.   placeHolder = [[Object alloc] init] ;
  229.   if((loadIndex = [self loadIndex: aClass]) == -1)
  230.   { // not loaded ;
  231.     [oldIdList addObject: placeHolder] ; // don't know id yet...
  232.     [nmList addObject: [NuString new: (char *) aClass]] ;
  233.   }
  234.   else
  235.   { // must unload it, and classes loaded 'after' it
  236.     knt = [loadList count] ;
  237.     for(i = loadIndex ; i < knt ; i++)
  238.     { ptr = (char *) [[loadList objectAt: i] cString] ;
  239.       [oldIdList addObject:objc_getClass(ptr)] ;
  240.       [nmList addObject:[NuString new: ptr]] ;
  241.     }
  242.     for(i = loadIndex ; i < knt ; i++)
  243.     { objc_unloadModules(NULL,NULL) ;
  244.       [[loadList removeLastObject] free] ; 
  245.     }
  246.   }
  247.   knt = [oldIdList count] ;
  248.   for(i = 0 ; i < knt ; i++)
  249.   { ptr = (char *) [[nmList objectAt: i] cString] ;
  250.     if(![self loadAux: ptr])
  251.     { NXRunAlertPanel("Nu","Can't load: %s\n"
  252.     "See Transcript for details.", NULL,NULL,NULL,ptr) ;
  253.       rval = nil ; 
  254.     }
  255.     else
  256.        [newIdList addObject: objc_getClass(ptr)] ;
  257.   }
  258.   [GlyphView become:newIdList if:oldIdList views: gVList] ;
  259.   [gVList makeObjectsPerform: @selector(display)] ;
  260.   [nmList makeObjectsPerform: @selector(free)] ;
  261.   [nmList free] ;
  262.   [oldIdList free] ;
  263.   [newIdList free] ;
  264.   [gVList free] ;
  265.   [placeHolder free] ;
  266.   [[Nu loadedClassesBrowser] loadColumnZero] ;
  267.   [[[Nu loadedClassesBrowser] matrixInColumn: 0]
  268.       scrollCellToVisible:[loadList count] - 1 :0] ;
  269.   return rval ;
  270. }
  271.  
  272. + (BOOL) loadAux:  (char *) aClass  ;
  273. { // try to load aClass. Return YES iff successful,
  274.   // else return NO.
  275.   const char *baseFile ;
  276.   char file[512], tempFile[512], superClass[256], *debugFile ;
  277.   const char *theFlags ;
  278.   NXStream *aStream ;
  279.   int i, moduleKnt ;
  280.   id linkList ;
  281.   // see if class is "hard loaded"
  282.   if([self isLoaded: aClass])
  283.     return YES ;
  284.   aStream = NXOpenMemory(NULL, 0, NX_READWRITE) ;
  285.   baseFile = NXGetDefaultValue([NXApp appName],"NuPath") ;
  286.   sprintf(file,"%s/.o/%s.o",baseFile,aClass);
  287.   if(!superClassName(file,superClass)) 
  288.   { [Nu printf: "Corrupt file: %s. Try recompiling it.\n", aClass] ;
  289.     return NO ;
  290.   }
  291.   if([self loadIndex: superClass] == -1)
  292.   { // superClass not loaded
  293.     if(![self loadAux: superClass])
  294.     { [Nu printf: "Couldn't load class %s, superclass of %s.\n",
  295.           superClass, aClass] ;
  296.       return NO ;
  297.     }
  298.   }
  299.   linkList = [NXGetNamedObject("PrefsManager",NXApp) linkList] ;
  300.   moduleKnt = [linkList count] ;
  301.   theFlags = NXGetDefaultValue([NXApp appName],"Flags") ;
  302.   if(theFlags[DEBUGFILES] - '0')
  303.   { sprintf(tempFile,"%s/.g/%s.g",baseFile,aClass) ;
  304.     debugFile = tempFile ;
  305.   }
  306.   else
  307.     debugFile = NULL ;
  308.   { char *modules[moduleKnt + 2] ;
  309.     for(i = 0 ; i < moduleKnt ; i++)
  310.       modules[i+1] = (char *) [[linkList objectAt: i] cString] ;
  311.     modules[0] = file ;
  312.     modules[i+1] = NULL ;
  313.     if(!objc_loadModules(modules, aStream, NULL, NULL,debugFile))
  314.     { [loadList addObject: [NuString new: aClass]] ;
  315.       NXCloseMemory(aStream,NX_TRUNCATEBUFFER) ;
  316.       return YES ;
  317.     }
  318.     else // load failed
  319.     { char *textBuf ;
  320.       int textLen, maxLen ;
  321.       NXGetMemoryBuffer(aStream, &textBuf, &textLen, &maxLen);
  322.       [Nu printf: "%s\n", textBuf] ;
  323.       NXCloseMemory(aStream,NX_TRUNCATEBUFFER) ;
  324.       return NO ;
  325.     }
  326.   }
  327. }
  328.  
  329.  
  330. + unload: (const char *) aClass ;
  331. { // unload aClass if it was incrementally loaded.
  332.   // returns nil if unable. Disallow unloading
  333.   // the "system" classes "Root","Glyph", "Tray",
  334.   // and "Error".
  335.   volatile List *nmList,*oldIdList, *newIdList, *gVList ;
  336.   volatile char *ptr ;
  337.   volatile id placeHolder, rval, rG ;
  338.   volatile int i , ind , knt  ;
  339.   volatile NXRect aRect ;
  340.   if(!strcmp(aClass,"Glyph") || !strcmp(aClass,"Root") 
  341.      || !strcmp(aClass,"Tray") || !strcmp(aClass,"Error"))
  342.   { NXRunAlertPanel("Nu","Error: you cannot unload class %s,\n"
  343.      "though you may edit, recompile, and reload it.",
  344.       NULL,NULL,NULL,aClass) ;
  345.     return self ;
  346.   }
  347.   rval = self ;
  348.   nmList = [List new] ;
  349.   oldIdList = [List new] ;
  350.   newIdList = [List new] ;
  351.   gVList = [List new] ;
  352.   placeHolder = [Object new] ;
  353.   ind = [self loadIndex: aClass] ;
  354.   if(ind == -1)
  355.     return nil ;
  356.   else
  357.   { // must unload it, and classes loaded 'after' it
  358.     knt = [loadList count] ;
  359.     for(i = ind  ; i < knt ; i++)
  360.     { [oldIdList addObject: objc_getClass((char *)[[loadList objectAt: i] cString])] ;
  361.       if(i == ind) // Will change existing aClass instances into Error glyphs
  362.         [nmList addObject: [NuString new: "Error"]] ;
  363.       else
  364.         [nmList addObject: [NuString new: (char *) [[loadList objectAt: i] cString]]] ;
  365.     }
  366.     for(i = ind ; i < knt ; i++)
  367.     { objc_unloadModules(NULL,NULL) ;
  368.       [[loadList removeLastObject] free] ; 
  369.     }
  370.   }
  371.   knt = [oldIdList count] ;
  372.   for(i = knt - 1 ; i >= 0 ; i--)
  373.   { ptr = (char *) [[nmList objectAt: i] cString] ;
  374.     if(![self loadAux: ptr])
  375.     { NXRunAlertPanel("Nu","Can't load: %s\n"
  376.     "See Transcript for details.", NULL,NULL,NULL,ptr) ;
  377.       rval = nil ; 
  378.     }
  379.     else
  380.       [newIdList insertObject: objc_getClass((char *) [[nmList objectAt: i] cString]) at: 0] ;
  381.   }
  382.   [GlyphView become:newIdList if:oldIdList views: gVList] ;
  383.   // redisplay all glyphViews which have glyph classes which have changed
  384.   knt = [gVList count] ;
  385.   for(i = 0 ; i < knt ; i++)
  386.   { rG = [[gVList objectAt: i] rootGlyph] ;
  387.     [rG getFrame: &aRect] ;
  388.     [rG display: &aRect from: nil] ;
  389.   }
  390.   [gVList makeObjectsPerform: @selector(display)] ;
  391.   [gVList free] ;
  392.   [nmList makeObjectsPerform: @selector(free)] ; 
  393.   [nmList free] ;
  394.   [oldIdList free] ;
  395.   [placeHolder free] ;
  396.   [[Nu loadedClassesBrowser] loadColumnZero] ;
  397.   return rval ;
  398. }
  399.  
  400. + unloadAll ;
  401. // unload all incrementally loaded classes, except the
  402. // "system" classes, which will always be the bottom 4.
  403. { int i, loadKnt ;
  404.   loadKnt = [loadList count] ;
  405.   for(i = 0 ; i < loadKnt - 4 ; i++)
  406.   { [[loadList lastObject] free] ;
  407.     [loadList removeLastObject] ;
  408.     objc_unloadModules(NULL,NULL) ;
  409.   }
  410.   [[Nu loadedClassesBrowser] loadColumnZero] ;
  411.   return self ;  
  412. }
  413.  
  414. +winList ;
  415. { return classWinList ;
  416. }
  417.  
  418.  
  419. - (int)browser:sender fillMatrix:matrix inColumn:(int)column ;
  420. { if(msgList)
  421.     return [msgList count] ;
  422.   else
  423.     return 0 ;
  424. }
  425.  
  426. - browser:sender loadCell:cell atRow:(int)row inColumn:(int)column ;
  427. { [cell setStringValue: [[msgList objectAt: row] cString]] ;
  428.   [cell setLeaf: YES] ;
  429.   return self ;
  430. }
  431.  
  432.  
  433. - className: (char *) name ;
  434. { // copy className into ivar
  435.   strncpy(className, name, 63) ;
  436.   return self ;
  437. }
  438.  
  439.  
  440. - classText: (char *) theText ;
  441. { // copy theText into text object
  442.   [textView setText: theText] ;
  443.   [self setDocEdited:YES];
  444.   return self ;
  445. }
  446.  
  447. - compile:sender ;
  448. { // returns nil if any errors
  449.   id rval ;
  450.   if([self isDocEdited]) // be sure file is saved first
  451.     [self save: self] ;
  452.   [self message: "Compiling..."] ;
  453.   rval = [ClassManager compileClass: className file: fileName] ;
  454.   [self message: ""] ;
  455.   return rval ;
  456. }
  457.  
  458.  
  459. - editSuperClass: sender ;
  460. { // attempt to open this class's superclass definition
  461.   char superClass[256] ;
  462.   char path[2048] ;
  463.   struct stat statBuf ;
  464.   // can't edit superclass of class Glyph!
  465.   if(!strcmp(className,"Glyph"))
  466.   {  NXRunAlertPanel("Nu",
  467.       "Error: can't edit class Object,\n"
  468.       "superclass of class Glyph\n",
  469.        NULL,NULL,NULL) ;
  470.      return nil ;
  471.   }
  472.   // create path to my .o file
  473.   sprintf(path,"%s/.o/%s.o",
  474.     NXGetDefaultValue([NXApp appName],"NuPath"),className) ;
  475.   // verify the path
  476.   if(stat(path,&statBuf))
  477.   {  NXRunAlertPanel("Nu",
  478.       "Error: can't find binary for class %s\n"
  479.       "Have you compiled it?",
  480.       NULL,NULL,NULL,className) ;
  481.      return nil ;
  482.   }
  483.   // get the superclass's name
  484.   if(!superClassName(path, &superClass[0]))
  485.   {  NXRunAlertPanel("Nu",
  486.       "Error: can't determine superclass of %s\n"
  487.       "Try recompiling %s",
  488.       NULL,NULL,NULL,className, className) ;
  489.      return nil ;
  490.   }
  491.   // (attempt to) edit the superclass
  492.   if(![[Nu glyphManager] editFile: nil class: superClass])
  493.   {  NXRunAlertPanel("Nu",
  494.       "Error: can't edit class %s,\n"
  495.       "superclass of %s.  Is it compiled?",
  496.       NULL,NULL,NULL,className, superClass) ;
  497.      return nil ;
  498.   }
  499.   return self ;
  500. }
  501.  
  502. - (char *) extension ;
  503. { // provide file extension
  504.   return "m" ;
  505. }
  506.  
  507. - format: sender ;
  508. { // format my text and update the browser
  509.   char textBuf[256], quotedBuf[513] ;
  510.   char *c, *d, fontStuff[5][5][40], defName[12] ;
  511.   const char *defaults ;
  512.   int i, len, charNum ; 
  513.   NXStream *rawStream, *cookedStream ;
  514.   [msgList freeObjects] ;
  515.   rawStream = [textView stream] ;
  516.   cookedStream = NXOpenMemory(NULL,0,NX_READWRITE) ;
  517.   NXPrintf(cookedStream,
  518.        "{\\rtf0\\ansi{\\fonttbl") ;
  519.   // get the font information from defaults database
  520.   for(i = 0 ; i < 5 ; i++)
  521.   { // Manufacture default name 
  522.     sprintf(defName,"classText%1d",i) ;
  523.     defaults = NXGetDefaultValue([NXApp appName],defName) ;
  524.     // parse the default value
  525.     sscanf(defaults,"%s %s %s %s %s %s",
  526.        textBuf, fontStuff[i][0], fontStuff[i][1],
  527.        fontStuff[i][2],fontStuff[i][3], fontStuff[i][4]) ;
  528.     NXPrintf(cookedStream,"\\f%1d\\fnil %s;", i,textBuf) ;
  529.  }
  530.  NXPrintf(cookedStream,"}\n") ;
  531.  NXPrintf(cookedStream,"{\\colortbl") ;
  532.  for(i = 0 ; i < 5 ; i++)
  533.  { NXPrintf(cookedStream,fontStuff[i][1]) ;
  534.    NXPrintf(cookedStream,";") ;
  535.  }
  536.  NXPrintf(cookedStream,"}\n") ;
  537.  
  538.  charNum = 0 ;
  539.  do
  540.  { NXSeek(rawStream, charNum, NX_FROMSTART) ;
  541.    NuGets(rawStream,textBuf) ;
  542.    for(c = textBuf, d = quotedBuf ; *c ;)
  543.    { if(*c == '{' || *c == '}' || *c == '\\' || *c == '-' || *c == '\n')
  544.      { // must quote these chars for rtf
  545.         *d++ = '\\' ;
  546.      }
  547.      if(*c == '/' && *(c+1) == '/')
  548.      { char tempStr[256] ;
  549.         // change style for slash-slash comment
  550.         sprintf(tempStr,"\\f3%s\\cf3%s%s%s",
  551.           fontStuff[3][0],fontStuff[3][2],
  552.          fontStuff[3][3], fontStuff[3][4]) ;
  553.         strcpy(d,tempStr) ;
  554.         (int) d += strlen(tempStr) ;
  555.       }
  556.       *d++ = *c++ ;
  557.     }
  558.     *d = '\0' ;
  559.     switch(textBuf[0])
  560.     { case '#': i = 0 ; break ;
  561.       case '@': i = 1 ; break ;
  562.       case '/':
  563.       case '-':
  564.       case '+': i = 2 ; break ;
  565.       default : i = 4 ; break ;
  566.      }
  567.      NXPrintf(cookedStream,
  568.        "\\f%1d%s\\cf%1d%s%s%s %s", i,
  569.         fontStuff[i][0], i,fontStuff[i][2],
  570.          fontStuff[i][3],fontStuff[i][4],quotedBuf) ;
  571.      if(i == 1 || i == 2) // add to msgList
  572.        [msgList addObject: [NuString new: textBuf]] ;
  573.     len = strlen(textBuf) ;
  574.     charNum += len ; // don't forget the newline
  575.   } while(!NXAtEOS(rawStream)) ;
  576.   NXSeek(cookedStream, 0, NX_FROMSTART) ;
  577.   /* uncomment to print stream to transcript 
  578.   { char *a ;
  579.     int t,m ;
  580.     NXGetMemoryBuffer(cookedStream, &a, &t, &m);
  581.     [Nu printf: a] ;
  582.   }
  583.   */
  584.   [textView readRichText: cookedStream] ;
  585.   NXCloseMemory(cookedStream,NX_TRUNCATEBUFFER) ;
  586.   [msgBrowser loadColumnZero] ;
  587.   return self ;
  588. }
  589.  
  590. - free ;
  591. { [msgList freeObjects] ;
  592.   [msgList free] ;
  593.   return [super free] ;
  594. }
  595.  
  596. - goToMsg: sender ;
  597. { // sender is a cell in the msgBrowser ; find
  598.   // the text in the textView
  599.   id manager ;
  600.   manager = NXGetNamedObject("FinderManager",NXApp) ;
  601.   if(manager) // should always be true
  602.   { [manager find: (char *) 
  603.        [[[sender matrixInColumn: 0] selectedCell]stringValue]
  604.     regEx: NO caseSensitive: YES inSelection: NO] ;
  605.   }
  606.   return self ;
  607. }
  608.  
  609. - init ;
  610. { // add my id to the classWinList 
  611.   [super init] ;
  612.   [classWinList addObject: self] ;
  613.   // assemble the pieces we get from the nib
  614.   [msgBrowser useScrollButtons: YES] ;
  615.   [splitView addSubview: [msgBrowser removeFromSuperview]] ;
  616.   [splitView addSubview: [[[textView superview] superview]
  617.       removeFromSuperview]] ;
  618.   // make sure we are not monofont
  619.   [self setMiniwindowIcon: "cmIcon.tiff"] ;
  620.   // create Storage for line numbers of
  621.   // lines in the msgBrowser
  622.   msgList = [List new] ;  
  623.   return self ;
  624. }
  625.  
  626.  
  627. - load:sender ;
  628. { [self message: "Loading..."] ;
  629.   [ClassManager load:className] ;
  630.   [self message: ""] ;
  631.   return self ;
  632. }
  633.  
  634. - (const char *) name ;
  635. { // redefine so that I can easily
  636.   // be identified
  637.   return className ;
  638. }
  639.  
  640. - readFile ;
  641. { NXStream *fileStream ;
  642.   if(fileName[0] == '\0')
  643.     return self ; // no file to read!
  644.   [self message: "Reading file..."] ;
  645.   if((fileStream = NXMapFile(fileName,NX_READONLY)) != NULL)
  646.   { [textView readText: fileStream] ;
  647.     NXCloseMemory(fileStream,NX_FREEBUFFER);
  648.     [self format: self] ; 
  649.     [self setDocEdited: NO] ;
  650.   }
  651.   else
  652.     NXRunAlertPanel("Nu", "Error, couldn't read: %s",
  653.         NULL,NULL,NULL,fileName) ;
  654.   [self message: ""] ;
  655.   return self ;
  656. }
  657.  
  658.  
  659. -(BOOL) saveTextToFileName ;
  660. // pre:  -ivar fileName contains a valid
  661. //       file pathname.
  662. //       -ivar textView contains a TextView object
  663. // post: if file can be opened or created, with mode
  664. //       644: text of textView is written out to the
  665. //       file named by fileName; file is closed, and
  666. //       YES is returned.
  667. //       otherwise returns NO
  668. { NXStream *aStream ;
  669.   [self message: "Saving file..."] ;
  670.   aStream = NXOpenMemory(NULL,0, NX_WRITEONLY);    
  671.   [textView writeText:aStream];    
  672.   NXSaveToFile(aStream, fileName);
  673.   NXCloseMemory(aStream, NX_FREEBUFFER);    
  674.   [self setDocEdited:NO];
  675.   [self message: ""] ;
  676.   return YES ;
  677. }
  678.  
  679. - update: sender ;
  680. { // save if needed, compile if needed, load if needed
  681.   struct stat mStat,oStat ;
  682.   const char *rootPath ;
  683.   char comBuf[512] ;
  684.   rootPath = NXGetDefaultValue([NXApp appName],"NuPath") ; 
  685.   if([self isDocEdited]) // be sure file is saved first
  686.     [self save: self] ;
  687.   stat(fileName,&mStat) ;
  688.   sprintf(comBuf,"%s/.o/%s.o",rootPath,className) ;
  689.   if(stat(comBuf,&oStat) || (mStat.st_mtime >= oStat.st_mtime)) 
  690.   { // no .o file or .o out of date 
  691.    if([self compile: self]) 
  692.      [self load: self] ; 
  693.   }
  694.   else if([ClassManager loadIndex: className] == -1)
  695.     [self load: self] ;
  696.   return self ;    
  697. }
  698.  
  699. - windowWillClose: sender ;
  700. { // remove myself from the classWinList
  701.   [classWinList removeObject: self] ;
  702.   return self ;
  703. }
  704.  
  705. - read: (NXTypedStream *) aStream ;
  706. { [super read: aStream] ;
  707.   NXReadArray(aStream, "c", 64, className) ;
  708.   return self ;
  709. }
  710.  
  711. - write: (NXTypedStream *) aStream ;
  712. { [super write: aStream ] ;
  713.   NXWriteArray(aStream, "c", 64, className) ;
  714.   return self ;
  715. }
  716.  
  717. @end
  718.